分类
联系方式
  1. 新浪微博
  2. E-mail

Racket 变量

define 全局绑定

(define x 1)

let 局部绑定

let 可以创建一个作用域,绑定多个变量,同时需要传入一个 body,在 body 中使用局部绑定。

(let ([a 3] [b 2])
     (+ a b))

let*

绑定间需要相互引用的时候,需要用 let*:

(let* ([x 10]
       [y (* x x)])
       (list x y))

let*-values

一个表达式返回多个值,给这些值都进行绑定:

(let*-values ([(x y) (quotient/remainder 10 3)])
             (list y x))

letrec 递归绑定

letrec 等效于如下形式:

(letrec ([id expr] ...) body ...+)

letrec 使得它的绑定对所有其它 expr 甚至早期的 expr 都可用。换句话说,letrec 的绑定是递归的。

letrec 中的 exprs 最常见的是递归和相互递归函数的lambda形式。

示例:

> (letrec ([swing
            (lambda (t)
              (if (eq? (car t) 'tarzan)
                  (cons 'vine
                        (cons 'tarzan (cddr t)))
                  (cons (car t)
                        (swing (cdr t)))))])
    (swing '(vine tarzan vine vine)))
'(vine vine tarzan vine)

其中:

  • 通过 letrec 定义了一个局部函数 swing
  • 在 body 中调用了该函数
  • 在 swing 实现中:
    • 如果列表第一个符号是 'tarzan
      • 返回一个新列表
        • 第一个符号是 'vine
        • 第二个符号是 'tarzan
        • 后面的是列表 cddr 获取后续元素
    • 否则
      • 返回一个新列表
        • 第一个元素是 t 的第一个元素
        • 其余元素再带入 swing 中进行递归处理

Named let

named let 是是一种迭代和递归形式。它使用与局部绑定先沟通的语法关键字 let,但是 let 后面的是标识符(而不是开括号),会触发不同的解析过程。语法定义如下:

(let proc-id ([arg-id init-expr] ...)
  body ...+)

named let 等效于:

(letrec ([proc-id (lambda (arg-id ...)
                     body ...+)])
  (proc-id init-expr ...))

也就是说,named let 绑定了一个只在函数主体中可见的函数标识符,它隐含地用一些初始表达式的值来调用该函数。 示例一:

(define (duplicate pos lst)
  (let dup ([i 0]
            [lst lst])
   (cond
     [(= i pos) (cons (car lst) lst)]
     [else (cons (car lst) (dup (+ i 1) (cdr lst)))])))
 
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
'("apple" "cheese burger!" "cheese burger!" "banana")

其中:

  • 用 named let 创建了 dup
  • 第一个 arg-id 是 i,init-expr 是 0
  • 第二个 arg-id 是 lst,init-expr 是 lst
  • 接下来的 cond 实际上就是以 i = 0、lst = lst 开始执行了
    • 如果 i == pos,把当前元素复制一个
    • 如果不等于,构建一个新列表,首元素 + 对其余元素递归调用 dup

示例二:

#lang racket
(define/contract (are-numbers-ascending s)
  (-> string? boolean?)
  (let* ([tokens (string-split s " ")]
         [numbers (filter (lambda (token)
                            (integer? (string->number token)))
                          tokens)])
    (if (empty? numbers)
        #t
        (let loop ([numbers numbers] [prev-num (string->number (first numbers))])
          (if (empty? (rest numbers))
              #t
              (let ([num (string->number (first (rest numbers)))])
                (if (< prev-num num)
                    (loop (rest numbers) num)
                    #f)))))))

其中:

  • 用 named let 创建了 loop
  • numbers = numbers、prev-number 是 numbers 中的第一个数
  • 如果遍历到头了(if (empty? (rest numbers))),说明满足递增,返回 true
  • 否则,取出下一个数命名为 num,将下一个数与当前数判断大小,如果不符合规则,直接给出结果,否则进行递归

网络资源

2 Racket语言概要 (tyrchen.github.io)

4.6 Local Binding (racket-lang.org)